package com.bkck.sharkblockade.fish;

import com.bkck.sharkblockade.levels.ball.Ball;
import com.bkck.sharkblockade.levels.lines.Line;
import com.bkck.sharkblockade.levels.lines.Point;
import com.bkck.sharkblockade.levels.thread.MainGamePanel;
import android.graphics.Bitmap;
import android.graphics.Canvas;

public class Fish {

  private Bitmap bitmap;

  private int fishHeight;
  private int fishWidth;

  private float pixelsPerMeter = 2.5f;

  private long lastUpdateTime = -1;

  private float x;
  private float originalX;
  private float y;
  private float xVelocity;
  private float originalXVelocity;

  private long startTime;
  private long delayedTime;
  private boolean delayed;
  private long pauseTime;

  private Line topLine;
  private Line bottomLine;
  private Line leftLine;
  private Line rightLine;

  public Fish(Bitmap bitmap) {
    this.bitmap = bitmap;
    this.fishHeight = bitmap.getHeight();
    this.fishWidth = bitmap.getWidth();
    this.x = MainGamePanel.viewWidth;
    originalX = x;
    randomize();
    delayed = true;
    originalXVelocity = xVelocity;
    startTime = System.currentTimeMillis();
    delayedTime = ((long) Math.random() * 5) * 1000;

    topLine = new Line(new Point(x, y), new Point(x + fishWidth, y));
    bottomLine = new Line(new Point(x, y + fishHeight), new Point(x + fishWidth, y + fishHeight));
    leftLine = new Line(new Point(x, y), new Point(x, y + fishHeight));
    rightLine = new Line(new Point(x + fishWidth, y), new Point(x + fishWidth, y + fishHeight));
  }

  public Fish() {

  }

  public Bitmap getBitmap() {
    return bitmap;
  }

  public void setBitmap(Bitmap bitmap) {
    this.bitmap = bitmap;
    fishHeight = bitmap.getHeight();
    fishWidth = bitmap.getWidth();
  }

  public float getPixelsPerMeter() {
    return pixelsPerMeter;
  }

  public void setPixelsPerMeter(float pixelsPerMeter) {
    this.pixelsPerMeter = pixelsPerMeter;
  }

  public long getLastUpdateTime() {
    return lastUpdateTime;
  }

  public void setLastUpdateTime(long lastUpdateTime) {
    this.lastUpdateTime = lastUpdateTime;
  }

  public int getHeight() {
    return fishHeight;
  }

  public int getWidth() {
    return fishWidth;
  }

  public float getX() {
    return x;
  }

  public void setX(int x) {
    this.x = x;
  }

  public float getY() {
    return y;
  }

  public void setY(int y) {
    this.y = y;
  }

  public void draw(Canvas canvas) {
    canvas.drawBitmap(bitmap, x, y, null);
  }

  public void updatePosition(Ball ball) {
    long currentTime = System.currentTimeMillis();
    if (!delayed) {
      if (lastUpdateTime < 0) {
        lastUpdateTime = currentTime;
        return;
      }

      long elapsedTime = currentTime - lastUpdateTime;
      lastUpdateTime = currentTime;

      // Fish is still on screen.
      if (x + fishWidth >= 0) {
        x = x - xVelocity * elapsedTime / 1000 * pixelsPerMeter;
      }
      // Fish is off screen so reset x and rerandomize y and x velocity.
      // Also set up startTime and delayedTime so that fish won't appear for up to 5 seconds.
      else {
        lastUpdateTime = -1;
        delayed = true;
        x = originalX;
        startTime = currentTime;
        delayedTime = ((long) Math.random() * 5) * 1000;
        randomize();
      }

      updateFishEdges();

      // Adjust velocity of ball if collision with the fish.
      if (!ball.getGhost() && isCloseTo(ball)) {
        collideWith(ball);
      }

    }
    else {
      if (currentTime - startTime >= delayedTime) {
        delayed = false;
      }
    }
  }

  public boolean isCloseTo(Ball ball) {
    // do some basic checks beforehand to avoid doing complex line intersection calculation if
    // possible
    // if ball is above the fish
    if (ball.getPreviousY() + ball.getHeight() < y && ball.getY() + ball.getHeight() < y) {
      return false;
    }
    // if ball is below the fish
    if (ball.getPreviousY() > y + fishHeight && ball.getY() > y + fishHeight) {
      return false;
    }
    // if ball is to the right of the fish
    if (ball.getPreviousX() > x + fishWidth && ball.getX() > x + fishWidth) {
      return false;
    }
    // if ball is to the left of the fish
    if (ball.getPreviousX() + ball.getWidth() < x && ball.getX() + ball.getWidth() < x) {
      return false;
    }
    return true;
  }

  private void collideWith(Ball ball) {
    Point intersection = null;

    // ball going down, check top of fish
    if (ball.getYVelocity() > 0) {
      // bottom left corner of the ball
      intersection =
          ball.findIntersection(topLine, ball.getPreviousX(),
              ball.getPreviousY() + ball.getHeight(), ball.getX(), ball.getY() + ball.getHeight());
      if (intersection != null) {
        ball.setX(intersection.getX());
        ball.setY(intersection.getY() - ball.getHeight());
        ball.setYVelocity(ball.getYVelocity() * -1);
        return;
      }

      // bottom right corner of the ball
      intersection =
          ball.findIntersection(topLine, ball.getPreviousX() + ball.getWidth(), ball.getPreviousY()
              + ball.getHeight(), ball.getX() + ball.getWidth(), ball.getY() + ball.getHeight());
      if (intersection != null) {
        ball.setX(intersection.getX() - ball.getWidth());
        ball.setY(intersection.getY() - ball.getHeight());
        ball.setYVelocity(ball.getYVelocity() * -1);
        return;
      }
    }

    // ball going up, check bottom of fish.
    if (ball.getYVelocity() < 0) {
      // top left corner of the ball
      intersection =
          ball.findIntersection(bottomLine, ball.getPreviousX(), ball.getPreviousY(), ball.getX(),
              ball.getY());
      if (intersection != null) {
        ball.setX(intersection.getX());
        ball.setY(intersection.getY());
        ball.setYVelocity(ball.getYVelocity() * -1);
        return;
      }

      // top right corner of the ball
      intersection =
          ball.findIntersection(bottomLine, ball.getPreviousX() + ball.getWidth(),
              ball.getPreviousY(), ball.getX() + ball.getWidth(), ball.getY());
      if (intersection != null) {
        ball.setX(intersection.getX() - ball.getWidth());
        ball.setY(intersection.getY());
        ball.setYVelocity(ball.getYVelocity() * -1);
        return;
      }
    }

    // ball going right, check left of fish
    if (ball.getXVelocity() > 0) {
      // top right corner of the ball
      intersection =
          ball.findIntersection(leftLine, ball.getPreviousX() + ball.getWidth(),
              ball.getPreviousY(), ball.getX() + ball.getWidth(), ball.getY());
      if (intersection != null) {
        ball.setX(intersection.getX() - ball.getWidth());
        ball.setY(intersection.getY());
        ball.setXVelocity(ball.getXVelocity() * -1);
        return;
      }

      // bottom right corner of the ball
      intersection =
          ball.findIntersection(leftLine, ball.getPreviousX() + ball.getWidth(),
              ball.getPreviousY() + ball.getHeight(), ball.getX() + ball.getWidth(), ball.getY()
                  + ball.getHeight());
      if (intersection != null) {
        ball.setX(intersection.getX() - ball.getWidth());
        ball.setY(intersection.getY() - ball.getHeight());
        ball.setXVelocity(ball.getXVelocity() * -1);
        return;
      }
    }

    // ball going left, check right of fish
    if (ball.getXVelocity() < 0) {
      // top left corner of the ball
      intersection =
          ball.findIntersection(rightLine, ball.getPreviousX(), ball.getPreviousY(), ball.getX(),
              ball.getY());
      if (intersection != null) {
        ball.setX(intersection.getX());
        ball.setY(intersection.getY());
        ball.setXVelocity(ball.getXVelocity() * -1);
        return;
      }

      // bottom left corner of the ball
      intersection =
          ball.findIntersection(rightLine, ball.getPreviousX(),
              ball.getPreviousY() + ball.getHeight(), ball.getX(), ball.getY() + ball.getHeight());
      if (intersection != null) {
        ball.setX(intersection.getX());
        ball.setY(intersection.getY() - ball.getHeight());
        ball.setXVelocity(ball.getXVelocity() * -1);
        return;
      }
    }
  }

  private void randomize() {
    xVelocity = ((float) Math.random() * 10) + 20;
    y = ((float) Math.random() * MainGamePanel.viewHeight);
    if (y >= MainGamePanel.viewHeight - 100) {
      y = MainGamePanel.viewHeight - 100 - fishHeight;
    }
  }

  /**
   * Updates the lines around the fish to its current location.
   */
  public void updateFishEdges() {
    topLine.getPoint1().setX(x);
    topLine.getPoint1().setY(y);
    topLine.getPoint2().setX(x + fishWidth);
    topLine.getPoint2().setY(y);
    bottomLine.getPoint1().setX(x);
    bottomLine.getPoint1().setY(y + fishHeight);
    bottomLine.getPoint2().setX(x + fishWidth);
    bottomLine.getPoint2().setY(y + fishHeight);
    leftLine.getPoint1().setX(x);
    leftLine.getPoint1().setY(y);
    leftLine.getPoint2().setX(x);
    leftLine.getPoint2().setY(y + fishHeight);
    rightLine.getPoint1().setX(x + fishWidth);
    rightLine.getPoint1().setY(y);
    rightLine.getPoint2().setX(x + fishWidth);
    rightLine.getPoint2().setY(y + fishHeight);
  }

  public void reset() {
    lastUpdateTime = -1;
    x = originalX;
    xVelocity = originalXVelocity;
    startTime = System.currentTimeMillis();
    delayedTime = ((long) Math.random() * 5) * 1000;
  }

  public void resume() {
    long currentTime = System.currentTimeMillis();
    if (delayed) {
      startTime = startTime + (currentTime - pauseTime);
    }
  }

  public void pause() {
    pauseTime = System.currentTimeMillis();
  }

}
